use std::{
    borrow::BorrowMut,
    collections::HashMap,
    sync::{Arc, Mutex},
};

use model_graph_types::{
    container::workflow::{
        SetIterationRoot, WorkflowBlock, WorkflowBlockBody, WorkflowBlockChain, WorkflowBlockRef,
        WorkflowBlockReturn, WorkflowDocument, WorkflowEngine, WorkflowEngineCompileOptions,
        WorkflowEngineRunOptions, WorkflowResult, WorkflowResultBuilder, WorkflowResultStatus,
    },
    modeling::Value,
};
use tokio::time::{sleep, timeout, Duration};

use crate::workflow::context::WorkflowContext;

use super::{
    state_machine::WorkflowStateMachine,
    state_machine_manager::{get_machine_result, remove_machine},
};

#[derive(Debug, Default)]
pub struct SimpleWorkflowEngine {}

#[async_trait::async_trait]
impl WorkflowEngine for SimpleWorkflowEngine {
    /// 根据
    async fn run_with_document(
        &self,
        document: WorkflowDocument,
        args: HashMap<String, Value>,
        compile_options: WorkflowEngineCompileOptions,
        run_options: WorkflowEngineRunOptions,
    ) -> anyhow::Result<String> {
        let block_chain = self.compile(document, compile_options).await?;

        let result = self.run(block_chain, args, run_options).await?;

        Ok(result)
    }

    /// 根据
    async fn run(
        &self,
        block_chain: WorkflowBlockChain,
        args: HashMap<String, Value>,
        options: WorkflowEngineRunOptions,
    ) -> anyhow::Result<String> {
        let mut context = WorkflowContext::new(options);
        //设置参数
        tracing::debug!("设置参数:{:?}", args);
        context.put_variables(args);

        let arc_machine = WorkflowStateMachine::new_and_start(block_chain, context)?;

        return Ok(arc_machine
            .lock()
            .map_err(|err| anyhow::anyhow!("{}", err))?
            .id());
    }

    /// 获取结果
    async fn result(&self, id: &String, remove: bool) -> anyhow::Result<Option<WorkflowResult>> {
        let result = get_machine_result(id)?;
        if remove && result.is_some() {
            remove_machine(id)?;
        }
        Ok(result)
    }
    /// 获取结果
    async fn result_with_timeout(
        &self,
        id: &String,
        remove: bool,
        time_out: u64,
    ) -> anyhow::Result<WorkflowResult> {
        if let Ok(response_result) = timeout(
            Duration::from_millis(time_out),
            Box::pin(async move {
                let mut workflow_result = WorkflowResult::with_failed();

                let mut result = Ok(None);
                loop {
                    result = SimpleWorkflowEngine::default().result(&id, true).await;
                    match result {
                        Ok(response) => {
                            if let Some(r) = response {
                                workflow_result = r;
                                break;
                            }
                        }
                        Err(err) => {
                            workflow_result.status = WorkflowResultStatus::Failed;
                        }
                    }
                    sleep(Duration::from_millis(100)).await;
                }
                workflow_result
            }),
        )
        .await
        {
            tracing::debug!("返回数据成功!");
            Ok(response_result)
        } else {
            tracing::debug!("请求超时!");
            Err(anyhow::anyhow!("请求超时!"))
        }
    }
    /// 运行并获取结果
    async fn fetch(
        &self,
        document: WorkflowDocument,
        args: HashMap<String, Value>,
        compile_options: WorkflowEngineCompileOptions,
        run_options: WorkflowEngineRunOptions,
    ) -> anyhow::Result<WorkflowResult> {
        //获取超时时间，毫秒
        let time_out = if let Some(t) = &run_options.timeout {
            *t
        } else {
            60000_u64
        };

        let block_chain = self.compile(document, compile_options).await?;
        let mut context = WorkflowContext::new(run_options);
        //设置参数
        tracing::debug!("设置参数:{:?}", args);
        context.put_variables(args);

        let arc_machine = WorkflowStateMachine::new_and_start(block_chain, context)?;
        //id
        let id = {
            arc_machine
                .lock()
                .map_err(|err| anyhow::anyhow!("{}", err))?
                .id()
        };

        if let Ok(response_result) = timeout(
            Duration::from_millis(time_out),
            Box::pin(async move {
                let mut workflow_result = WorkflowResult::with_failed();

                let mut result = Ok(None);
                loop {
                    result = SimpleWorkflowEngine::default().result(&id, true).await;
                    match result {
                        Ok(response) => {
                            if let Some(r) = response {
                                workflow_result = r;
                                break;
                            }
                        }
                        Err(err) => {
                            workflow_result.status = WorkflowResultStatus::Failed;
                        }
                    }
                    sleep(Duration::from_millis(100)).await;
                }
                workflow_result
            }),
        )
        .await
        {
            tracing::debug!("返回数据成功!");
            Ok(response_result)
        } else {
            tracing::debug!("请求超时!");
            Err(anyhow::anyhow!("请求超时!"))
        }
    }
    /// 根据
    async fn compile(
        &self,
        document: WorkflowDocument,
        options: WorkflowEngineCompileOptions,
    ) -> anyhow::Result<WorkflowBlockChain> {
        //
        let nodes = &document.graph.nodes;
        let edges = &document.graph.edges;

        let mut blocks_cache: HashMap<String, WorkflowBlockRef> = HashMap::new();
        let mut start_id: String = String::new();

        let mut iteration_list: Vec<(WorkflowBlockRef, String)> = vec![];

        //构建节点
        for node in nodes {
            let id = node.id.clone();
            if crate::workflow::blocks::start::is_start(&node.data) {
                start_id = id.clone();
            }
            let node_arc = Arc::new(Mutex::new(super::compiler::compile(&id, &node.data).await?));
            if node.is_iteration() {
                iteration_list.push((
                    node_arc.clone(),
                    node.iteration_start_id().map_or(
                        Err(anyhow::anyhow!("没有找到start id{}", node.id)),
                        |v| Ok(v),
                    )?,
                ));
            }

            blocks_cache.insert(id.clone(), node_arc);
        }
        //构建关系
        for edge in edges {
            let source = &edge.source;
            let source_handle = &edge.source_handle;
            let target = &edge.target;
            let target_handle = &edge.target_handle;

            let source_block = get_block(&blocks_cache, source)?;
            let target_block = get_block(&blocks_cache, target)?;

            let handle_key = format!("{}${}", source_handle, target_handle);
            //
            if let Some(source_node) = blocks_cache.get(source) {
                let t = source_node.clone();
                t.lock()
                    .map_err(|err| anyhow::anyhow!("{}", err))?
                    .borrow_mut()
                    .outputs
                    .insert(handle_key.clone(), target_block.clone());
            }
            if let Some(target_node) = blocks_cache.get(target) {
                let t = target_node.clone();
                t.lock()
                    .map_err(|err| anyhow::anyhow!("{}", err))?
                    .borrow_mut()
                    .inputs
                    .insert(handle_key.clone(), source_block.clone());
            }
            tracing::debug!("{}->{}", source, target)
        }

        //Iteration 处理
        for (iter, start_node_id) in iteration_list {
            let root = get_block(&blocks_cache, &start_node_id)?;
            iter.set_root(root.clone())?;
        }

        //查找开始节点
        let start = blocks_cache
            .get(&start_id)
            .map_or(Err(anyhow::anyhow!("没有发现开始节点!")), |v| Ok(v))?;

        let block_chain = WorkflowBlockChain {
            source: Some(document),
            root: start.clone(),
            index: blocks_cache,
        };
        return Ok(block_chain);
    }
}

fn get_block(
    cache: &HashMap<String, WorkflowBlockRef>,
    key: &String,
) -> anyhow::Result<WorkflowBlockRef> {
    if let Some(target_node) = cache.get(key) {
        Ok(target_node.clone())
    } else {
        Err(anyhow::anyhow!(""))
    }
}
